#pragma once
#include "Precompile.h"
#include "EngineEvents.h"
#include "MaterialManager.h"
#include "Model.h"
#include "SceneManager.h"
#include "ICamera.h"
#include "AnimationManager.h"
#include "SkySphere.h"
#include "LoadingHandle.h"

namespace Core{
	class ScriptManager;
}

namespace Graphics{
	class GUIManager;

	enum RenderSystemPlatform { RSP_DX10, RSP_DX9 };
	enum MultiSampleLevel { MS_1X, MS_2X, MS_4X, MS_8X, MS_16X, MS_8QX, MS_16QX };
	enum TextureFiltering { TF_LINEAR, TF_BILINEAR, TF_TRILINEAR, TF_ANISOTROPIC };
	
	struct RenderMode{
		std::pair<int,int> 	resolutionXY;
		MultiSampleLevel	multisampling;
		TextureFiltering 	texturefiltering;
		int					anisotropylevel,
							prerendercount,
							vsync;
		bool				fullscreen;
	};

	class RenderContext;

	class RenderSystem : public Core::MemTrack{
	public:
		RenderSystem(RenderMode &mode);
		virtual ~RenderSystem(){}

		virtual void		initialize(HWND hwnd) = 0;
		virtual void 		resetRenderSystem(RenderMode &mode) = 0;
		virtual void		saveRenderStates() = 0;
		virtual bool		restoreRenderStates() = 0;

		void				update();

		virtual void 		drawIndexed(UINT IndexCount,UINT StartIndexLocation,INT BaseVertexLocation) = 0;
		virtual void		draw(UINT numVerts, UINT startLoc) = 0;

		const RenderMode&	getCurrentRenderMode()	{ return m_renderMode; }
		
		unsigned int 		addModel(boost::shared_ptr<Model> model);
		unsigned int		addModel(boost::shared_ptr<Loading::LoadingHandle<Model>> modelHandle);
		void 				removeModel(unsigned int id);
		ModelPtr			getModel(unsigned int id);

		void				addMesh(boost::shared_ptr<Mesh> mesh);
		void				removeMesh(boost::shared_ptr<Mesh> mesh);

		void				setSkySphere(SkySpherePtr SkySphere) { m_SkySphere = SkySphere; }
		SkySpherePtr		getSkySphere() { return m_SkySphere; }

		int					registerSceneManager(SceneManagerPtr manager);
		void				unregisterSceneManager(SceneManagerPtr manager);
		void				unregisterSceneManager(unsigned int id);
		SceneManagerPtr		getSceneManager(unsigned int id);

		AnimationManagerPtr getAnimationaManager() { return m_animationManager; }
		PhysicsSystemPtr	getPhysicsSystem() { return m_physicsSystem; }

		void				registerCamera(ICameraPtr camera);
		void				unregisterCamera(ICameraPtr camera);

		void				registerGUIManager(boost::shared_ptr<GUIManager> gui);
		void				unregisterGUIManager(boost::shared_ptr<GUIManager> gui);

		boost::shared_ptr<RenderContext> getRenderContext() { return m_renderContext; }

		// Rendering events
		static Core::Event<RenderContext*> 	RenderingEvent;
		static Core::Event<>	 			PreRenderEvent,
											PostRenderEvent;

		bool				readyForUse() { return m_loadedAndReady; }
	protected:
		void cleanup();
		void resetDevice();
		void updateQueues();

		virtual void beginFrame() = 0;
		virtual void endFrame() = 0;

		// Lua wrappers
		LuaPlus::LuaObject getSceneManager_luaWrap(LuaPlus::LuaObject sceneManagerId);
		unsigned int loadModel_luaWrap(LuaPlus::LuaObject filename);
		void setSkyBox_luaWrap(	LuaPlus::LuaObject top, LuaPlus::LuaObject bottom, 
								LuaPlus::LuaObject left, LuaPlus::LuaObject right, 
								LuaPlus::LuaObject front, LuaPlus::LuaObject back);
		void unloadModel_luaWrap(LuaPlus::LuaObject modelId);
		void registerRenderUpdateFunction_luaWrap(LuaPlus::LuaObject funcName);
		void unregisterRenderUpdateFunction_luaWrap(LuaPlus::LuaObject funcName);

		void unregisterLuaState(LuaPlus::LuaState* state);

		std::map<LuaPlus::LuaState*,std::list<std::string>> m_renderUpdateFunctions;
		std::map<LuaPlus::LuaState*,std::set<int>> m_luaStateModels;
		std::map<LuaPlus::LuaState*,SkySpherePtr> m_luaSkySpheres;

		RenderingDeviceHandle 	m_deviceHandle;
		RenderMode				m_renderMode;

		boost::shared_ptr<boost::timer> m_frameTimer;
		double m_dt;

		SkySpherePtr m_SkySphere;

		PhysicsSystemPtr m_physicsSystem; 
		std::set<boost::shared_ptr<GUIManager>> m_GUIManagers;
		boost::shared_ptr<AnimationManager> m_animationManager;
		boost::shared_ptr<MaterialManager> m_materialManager;
		boost::shared_ptr<RenderContext> m_renderContext;
		std::map<unsigned int,SceneManagerPtr> m_sceneManagers;
		std::map<SceneManagerPtr,unsigned int> m_sceneManagersRevMap;
		std::set<ICameraPtr> m_cameras;

		std::map<std::string,unsigned int> m_modelsNameToIdMap;
		std::map<unsigned int, ModelPtr> m_models;
		std::map<unsigned int, boost::shared_ptr<Loading::LoadingHandle<Model>>> m_modelHandles;
		std::vector<MeshPtr> m_addMeshQueue;
		std::vector<MeshPtr> m_removeMeshQueue;

		std::map<unsigned int,std::pair<std::list<unsigned int>, std::list<unsigned int>>> m_transparentModels;

		unsigned int	m_modelId,
						m_sceneManagerId;

		bool m_loadedAndReady;

		MutexPtr	m_guiMutex,
					m_removeMeshQueueMutex,
					m_addMeshQueueMutex,
					m_modelHandlesMutex;

		friend class Core::ScriptManager;
		friend class Graphics::Model;
	};

	class RenderContext{
	public:
		virtual ~RenderContext(){
		}

		double 					m_dt;
		RenderSystem*			m_renderSystem;
		RenderingDeviceHandle	m_handle;
		MaterialManager*		m_materialManager;
		AnimationManager*		m_animationManager;
	};
}

typedef boost::shared_ptr<Graphics::RenderContext> RenderContextPtr;
typedef boost::shared_ptr<Graphics::RenderSystem> RenderSystemPtr;